<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    .cvs{
      border: solid 1px black;
    }
  </style>
</head>
<body>
  <canvas class="cvs"></canvas>
</body>
<script>

  const cvs = document.querySelector(".cvs");
  cvs.width = 600;
  cvs.height = 300;

  const ctx = cvs.getContext("2d");

  drawLine({
    title:"季度销量",
    xAxis: ["第一季度","第二季度","第三季度","第四季度"],
    data: [30, 400, 300, 25],
    lineColor: "#f00",
    arcColor: "#0f0",
    textColor: "#00f"
  });

  function drawLine({title, xAxis, data, lineColor, arcColor, textColor}){
    // 坐标轴的原点
    const originPos = {x: 20, y: 40};

    // 绘制坐标轴
    const length = axis( originPos );

    // 用于处理数据（记录每个数据对应的x和y坐标）
    const msg = [];

    // 绘制x轴标记
    xAxis.forEach((val, idx)=>{
      const x = length.x / (xAxis.length+1) * (idx+1) + originPos.x;
      ctx.moveTo(x, cvs.height-40)
      ctx.lineTo(x, cvs.height-50)
      ctx.stroke();
      ctx.font = "16px 黑体";
      ctx.textAlign = "center";
      ctx.fillText(val, x, cvs.height-20);

      msg.push({
        x: x,
        data: data[idx]
      })
    })

    const max = Math.max(...data) + 20;

    // 画线
    msg.forEach((val,idx)=>{
      const y = length.y / max * val.data + 20;
      if(idx === 0){
        ctx.beginPath();
        ctx.moveTo(val.x, cvs.height - y - 30);
      }
      ctx.lineTo(val.x, cvs.height - y - 30);
      ctx.strokeStyle = lineColor;
      ctx.stroke();

      val.y = y + 30;
    })

    // 画端点和文字
    msg.forEach(val=>{
      ctx.beginPath();
      ctx.arc(val.x, cvs.height - val.y, 5, 0, 2*Math.PI);
      ctx.fillStyle = arcColor;
      ctx.fill();

      ctx.fillStyle = textColor;
      ctx.fillText(val.data, val.x, cvs.height - val.y-10);
    })

    // 标题
    ctx.font = "20px 黑体";
    ctx.textBaseline = "top";
    ctx.fillText(title, cvs.width/2, 10);
  }

  // 绘制坐标轴
  function axis( {x, y} ){
    ctx.lineWidth = 2;
    ctx.moveTo(x, cvs.height-y);
    ctx.lineTo(cvs.width-20, cvs.height-y);
    ctx.stroke();

    ctx.moveTo(cvs.width-20, cvs.height-y-4)
    ctx.lineTo(cvs.width-20, cvs.height-y+4);
    ctx.lineTo(cvs.width-10, cvs.height-y);
    ctx.fill();

    ctx.beginPath();
    ctx.moveTo(x, cvs.height-y);
    ctx.lineTo(x, 60);
    ctx.stroke();

    ctx.moveTo(x-4, 60)
    ctx.lineTo(x+4, 60);
    ctx.lineTo(x, 50);
    ctx.fill();

    return {
      x: cvs.width-20-x,
      y: cvs.height - y - 80,
    }
  }
  
</script>
</html>